package cn.cdm.interceptor;

import cn.cdm.domain.MiaoshaUser;
import cn.cdm.service.MiaoshaUserService;
import cn.cdm.util.ApplicationContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 登录拦截器，统一拦截所有请求，只有当请求已经登录，才会放行该请求；否则，跳转到登录页面。
 *
 * 靠前端实现的拦截很容易被绕过。所以要想实现靠谱的拦截，必须由后端验证用户登录状态。
 *   这个思路并不难，就是前端带上 sessionId 发送请求交由后端 认证，坑爹之处主要在于前后端分离的情况下需要额外的配置解决跨域问题。
 *   默认的情况下，跨域的 cookie 是被禁止的，后端不能设置，前端也不能发送，所以两边都要设置。
 * 原文链接：https://blog.csdn.net/Neuf_Soleil/article/details/102788866
 *
 * @author qinxubang
 * @Date 2021/5/1 10:05
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 通过ApplicationContextUtils工具类，获取IOC容器中的MiaoshaUserService bean对象
     */
    private MiaoshaUserService miaoshaUserService = ApplicationContextUtils.getBean(MiaoshaUserService.class);


    /**
     * 用户访问URL，检测是否为登录页面，如果是登录页面则·不进行拦截，也就是beginWith(page, requireAuthPages)为false
     * 如果用户访问的不是登录页面，我们判断session对象中是否存在用户信息，如果不存在，也就是用户未登录，则重定向到登录页面。
     */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        String requestURI = httpServletRequest.getRequestURI();
        log.info("preHandle拦截的请求路径是: {}",requestURI);

        // 放行 options 请求，否则无法让前端带上自定义的 header 信息，导致 sessionID 改变，shiro 验证失败
        if (HttpMethod.OPTIONS.toString().equals(httpServletRequest.getMethod())) {
            httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
            log.info("preHandle#OPTIONS请求方式");
            return true;
        }

        // 登录检查逻辑
        // 先获取请求中的cookie，根据cookie来获取sessionId，然后根据sessionId，去Redis中查找对应的用户对象信息
        String cookieName = MiaoshaUserService.COOKI_NAME_TOKEN;
        String sessionId = getCookieValue(httpServletRequest, cookieName);

        if (StringUtils.isEmpty(sessionId)) {
            log.error("该cookieName对应的sessionId为空，cookieName={}；本次请求对象：{}", cookieName, httpServletRequest);
            // 服务器内部转发
            httpServletRequest.getRequestDispatcher("/").forward(httpServletRequest,httpServletResponse);

            return false;
        }

        // 根据sessionId，去Redis中查找对应的用户对象信息
        MiaoshaUser miaoshaUser = miaoshaUserService.getByToken(httpServletResponse, sessionId);
        if (miaoshaUser == null) {
            log.error("miaoshaUser为空；本次请求对象：{}", httpServletRequest);
            // 服务器内部转发
            httpServletRequest.getRequestDispatcher("/").forward(httpServletRequest,httpServletResponse);
            return false;

        } else {
            log.info("用户已登录，拦截器直接放行：{}；{}", miaoshaUser.getNickname(), requestURI);
            return true;
        }

    }

    /**
     * 根据cookieName，查找该cookieName对应的值
     * @param httpServletRequest
     * @param cookieName
     * @return
     */
    private String getCookieValue(HttpServletRequest httpServletRequest, String cookieName) {
        Cookie[] cookies = httpServletRequest.getCookies();
        if (cookies != null && cookies.length > 0) {
            // 遍历数组，找到cookieName，然后根据cookieName，查找对应的value值（即sessionId）
            for (Cookie cookie : cookies) {
                if (cookieName.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }

        return null;
    }

}



































